﻿using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Extensions
{
    public static class ExtensionMethods
    {
        public static string Repeat(this string str, int times)
        {
            string returnStr = "";
            for (int i = 0; i < times; i++)
            {
                returnStr += str;
            }
            return returnStr;
        }

        public static Vector2 Abs(this Vector2 vector2)
        {
            return new Vector2(Mathf.Abs(vector2.x), Mathf.Abs(vector2.y));
        }

        public static Vector2 CalFullSize(this GUIStyle style, string str)
        {
            return style.CalFullSize(new GUIContent(str));
        }

        // 和 GUIHelper.MarginBox 配套使用，利用GUI计算带margin值的Size
        public static Vector2 CalFullSize(this GUIStyle style, GUIContent content)
        {
            Vector2 size = style.CalcSize(content);
            size.x += style.margin.horizontal;
            size.y += style.margin.vertical;
            return size;
        }

        public static Vector2 CalBoundSize(this GUIStyle style, Vector2 contentSize)
        {
            contentSize.x += (style.margin.horizontal + style.padding.horizontal);
            contentSize.y += (style.margin.vertical + style.padding.vertical);
            return contentSize;
        }

        public static RectOffset Multiply(this RectOffset rectOffset, float num)
        {
            return new RectOffset((int) Mathf.Round(rectOffset.left * num), (int) Mathf.Round(rectOffset.right * num),
                (int)Mathf.Round(rectOffset.top * num), (int)Mathf.Round(rectOffset.bottom * num));
        }

        public static Rect Multiply(this Rect rect, float num)
        {
            return new Rect(rect.position * num, rect.size * num);
        }

        public static bool IsList(this Type type)
        {
            return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>);
        }

        public static bool IsList(this Type type, Type genericType)
        {
            return IsList(type) && type.GenericTypeArguments[0].IsSubclassOf(genericType);
        }

        public enum DockPosition
        {
            Left,
            Right,
            Top,
            Bottom
        }

        public static void DockWindow(this EditorWindow parent, EditorWindow child, DockPosition dockPosition)
        {
            Assembly assembly = typeof(EditorWindow).Assembly;
            Type reflectDockArea = assembly.GetType("UnityEditor.DockArea");
            Type reflectSplitView = assembly.GetType("UnityEditor.SplitView");

             // the Dockarea we're inside, HostView type
             FieldInfo ew_m_parent = typeof(EditorWindow).GetField("m_Parent", BindingFlags.NonPublic | BindingFlags.Instance);
            var parentDockArea = ew_m_parent.GetValue(parent);
            var childDockArea = ew_m_parent.GetValue(child);

            // parent view
            PropertyInfo da_parent = reflectDockArea.GetProperty("parent", BindingFlags.Public | BindingFlags.Instance);
            var parentDockAreaView = da_parent.GetValue(parentDockArea);
            var childDockAreaView = da_parent.GetValue(childDockArea);

            // parent view screen rect 
            PropertyInfo sv_screenPosition = reflectSplitView.GetProperty("screenPosition", BindingFlags.Public | BindingFlags.Instance);
            Rect parentScreenPosition = (Rect)sv_screenPosition.GetValue(parentDockAreaView);

            // dock height
            var da_kDockHeight = reflectDockArea.GetField("kDockHeight", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
            float kDockHeight = (float)da_kDockHeight.GetRawConstantValue();

            Vector2 simMouseScreenPosition = Vector2.zero;
            switch (dockPosition)
            {
                case DockPosition.Left:{ simMouseScreenPosition = new Vector2(parentScreenPosition.x + 1f, parentScreenPosition.y + parentScreenPosition.height / 2); break; }
                case DockPosition.Right:{ simMouseScreenPosition = new Vector2(parentScreenPosition.x + parentScreenPosition.width - 1f, parentScreenPosition.y + parentScreenPosition.height / 2); break; }
                case DockPosition.Top: { simMouseScreenPosition = new Vector2(parentScreenPosition.x + parentScreenPosition.width / 2, parentScreenPosition.y + kDockHeight + 1f); break; }
                case DockPosition.Bottom:{ simMouseScreenPosition = new Vector2(parentScreenPosition.x + parentScreenPosition.width / 2, parentScreenPosition.y + parentScreenPosition.height - 1f); break; }
            }

            MethodInfo sv_DragOver = reflectSplitView.GetMethod("DragOver", BindingFlags.Public | BindingFlags.Instance);
            var dropInfo = sv_DragOver.Invoke(parentDockAreaView, new object[] { child, simMouseScreenPosition });

            var userData = assembly.GetType("UnityEditor.DropInfo").GetField("userData", BindingFlags.Public | BindingFlags.Instance);
            var dropinfo_userData = userData.GetValue(dropInfo);

            // where it come from
            FieldInfo da_s_OriginalDragSource = reflectDockArea.GetField("s_OriginalDragSource", BindingFlags.NonPublic | BindingFlags.Static);
            da_s_OriginalDragSource.SetValue(null, childDockArea);

            MethodInfo sv_PerformDrop = reflectSplitView.GetMethod("PerformDrop", BindingFlags.Public | BindingFlags.Instance);
            sv_PerformDrop.Invoke(parentDockAreaView, new object[] { child, dropInfo, null });
        }

        public static string FriedlyName(this Type type)
        {
            if (type == typeof(int)) { return "int"; }
            if (type == typeof(float)) { return "float"; }
            if (type == typeof(long)) { return "long"; }
            if (type == typeof(double)) { return "double"; }
            if (type == typeof(string)) { return "string"; }
            if (type == typeof(bool)) { return "boolean"; }
            if (type == typeof(Vector3)) { return "Vector3"; }
            if (type == typeof(GameObject)) { return "GameObject"; }
            if (type == typeof(List<int>)) { return "List<int>"; }
            if (type == typeof(List<float>)) { return "List<float>"; }
            if (type == typeof(List<long>)) { return "List<long>"; }
            if (type == typeof(List<double>)) { return "List<double>"; }
            if (type == typeof(List<string>)) { return "List<string>"; }
            if (type == typeof(List<bool>)) { return "List<boolean>"; }
            if (type == typeof(List<GameObject>)) { return "List<GameObject>"; }
            if (type == typeof(List<Vector3>)) { return "List<Vector3>"; }
            return type.Name;
        }
    }

    public static class GUIHelper
    {
        public static Rect MarginBox(Rect rect, GUIContent content, GUIStyle style)
        {
            Rect realRect = new Rect(rect.position.x + style.margin.left,
                rect.position.y + style.margin.top,
                rect.size.x - style.margin.horizontal,
                rect.size.y - style.margin.vertical);
            GUI.Box(realRect, content, style);
            return realRect;
        }
    }

    public static class GUILayoutHelper
    {
        public static void SeparatorLine()
        {
            GUILayout.Box("", TC.StyleProvider.separatorLineStyle);
        }
    }

    public static class VariableHelper
    {
        public static Type Convert(TC.VarType varType)
        {
            switch (varType)
            {
                case TC.VarType.Int: { return typeof(int); }
                case TC.VarType.Float: { return typeof(float); }
                case TC.VarType.Long: { return typeof(long); }
                case TC.VarType.String: { return typeof(string); }
                case TC.VarType.Bool: { return typeof(bool); }
                case TC.VarType.Vector3: { return typeof(Vector3); }
                case TC.VarType.GameObject: { return typeof(GameObject); }
                case TC.VarType.IntArray: { return typeof(List<int>); }
                case TC.VarType.FloatArray: { return typeof(List<float>); }
                case TC.VarType.LongArray: { return typeof(List<long>); }
                case TC.VarType.StringArray: { return typeof(List<string>); }
                case TC.VarType.BoolArray: { return typeof(List<bool>); }
                case TC.VarType.Vector3Array: { return typeof(List<Vector3>); }
                case TC.VarType.GameObjectArray: { return typeof(List<GameObject>); }
                default: { return typeof(object); }
            }
        }
    }
}
